#ifndef __PROCESS_POOL_HPP__
#define __PROCESS_POOL_HPP__

#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
#include <functional>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <ctime>
#include "Task.hpp"

const int gdefault_process_num = 5;
// c++11定义一个类型别名
// std::function<void(int)> 是 C++11 引入的一个通用函数包装器
using callback_t = std::function<void(int fd)>;

// 2
class Channel
{
public:
    // 构造
    Channel()
    {
    }
    Channel(int fd, std::string &name, pid_t id)
        : _wfd(fd), _name(name), _sub_target(id)
    {
    }

    // 析构
    ~Channel() {}
    int Fd() { return _wfd; }
    std::string Name() { return _name; }
    pid_t Target() { return _sub_target; }

    // 关闭信道直接封装系统调用关闭id就可以
    void Close() { close(_wfd); }

    // 阻塞等待回收子进程
    void Wait()
    {
        pid_t rid = waitpid(_sub_target, nullptr, 0);
        (void)rid;
    }

private:
    // 要管理它得知道每个信道id，名字、对应哪一个子进程
    int _wfd;
    std::string _name;
    pid_t _sub_target; // 目标子进程是谁
};

class ProcessPool
{
private:
    void CtrlSubProcessHelper(int &index)
    {
        // 1. 选择一个通道(进程)
        int who = index;
        index++;
        index %= _chanels.size();
        //随机选择一个任务
        int x = rand() % tasks.size();

        //任务推送给子进程
        std::cout<<"选择信道："<<_chanels[who].Name()<<", subtarget: "<<_chanels[who].Target()<<std::endl;
        write(_chanels[who].Fd(),&x,sizeof(x));
        sleep(1);
    }
public:
    // 构造
    ProcessPool(int num = gdefault_process_num) : _processnum(num)
    {
        // 随机种子
        srand(time(nullptr) ^ getpid() ^ 0x777);
    }
    // 析构
    ~ProcessPool() {}

    // 3.初始化进程池
    bool InitProcessPool(callback_t cb)
    {
        for (int i = 0; i < _processnum; i++)
        {
            // 系统调用创建进程池
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)return false;

            //创建子进程
            pid_t id = fork();
            if(id<0)return false;
            if(id==0)
            {
                //子进程读
                close(pipefd[1]);
                //回调函数，阻塞等待
                cb(pipefd[0]);
                exit(1);

            }
            //父进程写
            close(pipefd[0]);
            std::string name = "channel:-"+std::to_string(i);
            //初始化要把数据写入数组中才算初始化结束
            _chanels.emplace_back(pipefd[1],name,id);
        }
        return true;
    }
    //4.控制唤醒指定的一个子进程，让该子进程完成指定任务
    //轮询选择一个子进程(选择一个信道) -- 负载均衡
    void PollingCtrlSubProcess()
    {
        int index = 0;
        while(true)
        {
             CtrlSubProcessHelper(index);
        }
    }
    void PollingCtrlSubProcess(int count)
    {
        if(count<0)return;
        int index=0;
        while(count)
        {
            CtrlSubProcessHelper(index);
            count--;
        }
    }
    void WaitSubProcess()
    {
        for(auto& c: _chanels)
        {
            c.Close();
            c.Wait();
        }
    }

private:
    // 1.先定成员变量，跳过写信道类
    // 进程池一个父进程有多个信道对应多个子进程
    // 所以要有信道和子进程个数，信道用一个数组描述起来
    // 数组中每个元素是一个Chanl信道
    std::vector<Channel> _chanels;
    int _processnum;
};

#endif